'''
@author: amm
'''

import output
import util
import dshell
import sys
import cgi
import string
import datetime


class ColorOutput(output.TextOutput):

    '''
    Color-coded Output module
    use with --output=colorout
    Output to STDOUT will use XTERM color tags, if possible.
    Output to FILE will use HTML

    Decoders should call self.out.write() with string data and the following kwargs:
            formatTag:  H1 and H2 are currently implemented
            direction:  cs / sc
            timestamp:  specify unix timestamp for current object being written
            time:       (bool) to display timestamp

    Note Regarding Timestamps:
    -------------------------
    Decoders should *always* specify timestamp information if available in their
    calls to write.  (If passing a full blob or connection, colorout will extract
    this information from those objects.)  In HTML output mode, the timestamps will
    always be embedded in the HTML with a javascript option to show/hide them.
    Initial display is govered by the boolean kwarg 'time' specified in calls
    to write().  (Defaults to hidden unless a single 'true' value is passed.)

    Instantiation options
    ---------------------
    keyword title: specify HTML title
    keyword force: specify force=true color output (e.g for piping to less -R)
    keyword html:  specify html=true for HTML output, even when writing to STDOUT

    HTML Generator Mode:
            This mode makes the HTML generation/formatting available
            as a utility to other code:
            1) Instantiate with keyword htmlgenerator=True:
                  colorout.ColorOutput(htmlgenerator=True, title="test")
            2) After one or more calls to write(), call close()
            3) Dump HTML with htmldump()

    '''
    # COLORMODEs:
    #   TEXT - Plain text.  No color coding.  (Default)
    #   TTY - TTY style color encoding.
    #   HTML - HTML/CSS output
    _COLORMODE = 'TEXT'

    # Offsets for HEX Mode
    _CS_offset = 0
    _SC_offset = 0
    _NODIR_offset = 0

    # Custom error handler for data reassembly --- ignores all errors
    def errorH(self, **x):
        return True

    def __init__(self, *args, **kwargs):

        if 'format' in kwargs:
            fmtstr = kwargs['format']
            del kwargs['format']  # don't let base class process this
        else:
            fmtstr = ''

        # Title
        if 'title' not in dir(self):
            if 'title' in kwargs:
                self.title = kwargs['title']
                del kwargs['title']  # don't let base class process this
            else:
                self.title = 'Dshell'

        # Check for html generator mode
        if 'htmlgenerator' not in dir(self):
            if 'htmlgenerator' in kwargs:
                self.htmlgenerator = kwargs['htmlgenerator']
                # don't let base class process this
                del kwargs['htmlgenerator']
            else:
                self.htmlgenerator = False
        if self.htmlgenerator:
            self.htmlbuffer = ''

        # Check for force color mode
        if 'force' not in dir(self):
            if 'force' in kwargs:
                self.force = True
                del kwargs['force']  # don't let base class process this
            else:
                self.force = False

        # Override HTML for stdout
        if 'html' not in dir(self):
            if 'html' in kwargs:
                self.html = True
                del kwargs['html']
            else:
                self.html = False

        # Call parent init
        output.TextOutput.__init__(self, format=fmtstr, **kwargs)

        self.setColorMode()

        # Write Header
        if self._COLORMODE == 'HTML':
            self._htmlwrite(self._HTMLHeader(self.title))

        # In HTML mode, if we get any single call
        # to write() with the time option set, we will set
        # this master boolean value to True and display all timestamps
        # on initial page load.  Otherwise, timestamps will be hidden
        # until toggled by the user
        self._htmldisplaytimes = False

    def setColorMode(self):
        # Determine output mode
        if self.fh == sys.stdout and not self.htmlgenerator and not self.html:
            # STDOUT.  Use Text Based Mode.
            if sys.stdout.isatty() or self.force:
                self._COLORMODE = 'TTY'
            else:
                self._COLORMODE = 'TEXT'
        else:
            # File.  Use HTML.
            self._COLORMODE = 'HTML'

    # Internal function to write HTML
    # or buffer it in factory mode
    def _htmlwrite(self, text):
        if self.htmlgenerator:
            self.htmlbuffer += text
        else:
            self.fh.write(text)

    def htmldump(self):
        '''
        For use in HTML Generator Mode:
        In this mode, HTML generated by calls to write() is buffered.  This
          function returns the contents of and clears the buffer.
        '''
        tmp = self.htmlbuffer
        self.htmlbuffer = ''
        return tmp

    def close(self):
        # Footer
        if self._COLORMODE == 'HTML':
            self._htmlwrite(self._HTMLFooter())
        output.TextOutput.close(self)

    def _reset_offsets(self, value=0):
        self._CS_offset = value
        self._SC_offset = value
        self._NODIR_offset = value

    def write(self, *args, **kw):

        # KW Options
        if 'hex' in kw and kw['hex']:
            self._hexmode = True
        else:
            self._hexmode = False
        if 'time' in kw and kw['time']:
            self._timemode = True
            self._htmldisplaytimes = True
        else:
            self._timemode = False

        # KW formatTag
        if 'formatTag' in kw:
            formatTag = kw['formatTag']
            del kw['formatTag']
        else:
            formatTag = ''

        # KW Direction
        if 'direction' in kw:
            kwdirection = kw['direction']
        else:
            kwdirection = ''

        # KW Offset
        if 'offset' in kw:
            self._reset_offsets(kw['offset'])

        # KW Timestamp
        if 'timestamp' in kw and kw['timestamp'] != None:
            kwtimestamp = kw['timestamp']
        else:
            kwtimestamp = None

        # KW Encoding (to specify character encoding)
        if 'encoding' in kw and kw['encoding'] != None:
            kwencoding = kw['encoding']
        else:
            kwencoding = None

        # FormatTag (HTML)
        if len(formatTag) and self._COLORMODE == 'HTML':
            self._htmlwrite('<%s>' % formatTag)

        # Iterate *args
        for a in args:
            if type(a) == dshell.Blob:
                self._write_blob(a, kwencoding)
            elif type(a) == dshell.Connection:
                self._reset_offsets()
                for blob in a:
                    self._write_blob(blob, kwencoding)
            else:
                self._write_string(a, kwdirection, kwtimestamp, kwencoding)

        # Format Post Tag
        if len(formatTag) and self._COLORMODE == 'HTML':
            self._htmlwrite('</%s>' % formatTag)

    def _write_blob(self, blob, encoding=None):
        self._write_string(
            blob.data(errorHandler=self.errorH), blob.direction, blob.starttime, encoding)

    def _write_string(self, text, direction, timestamp, encoding=None):

        colorTag = ''

        # Print TimestampcolorTag
        if self._COLORMODE == 'HTML' and timestamp != None:
            self._htmlwrite('<div class="timestamp">\n%s UTC:</div>' %
                            datetime.datetime.utcfromtimestamp(timestamp))
            #if self._hexmode: self._htmlwrite("<br>")
        elif self._COLORMODE == 'TTY' and self._timemode and timestamp != None:
            self.fh.write('\x1b[36m%s UTC:\x1b[0m\n' %
                          datetime.datetime.utcfromtimestamp(timestamp))

        # Set Direction
        if direction.lower() == 'cs':
            if self._COLORMODE == 'HTML':
                self._htmlwrite('<span style="color:red;">')
            elif self._COLORMODE == 'TTY':
                colorTag = '\x1b[0;31m'
        elif direction.lower() == 'sc':
            if self._COLORMODE == 'HTML':
                self._htmlwrite('<span style="color:blue;">')
            elif self._COLORMODE == 'TTY':
                colorTag = '\x1b[0;32m'

        # Hex Mode Data
        if self._hexmode:
            # Hex Output
            dlen = len(text)
            if direction.lower() == 'cs':
                msgOffset = self._CS_offset
                self._CS_offset += dlen
            elif direction.lower() == 'sc':
                msgOffset = self._SC_offset
                self._SC_offset += dlen
            else:
                msgOffset = self._NODIR_offset
                self._NODIR_offset += dlen
            text = util.hexPlusAscii(str(text), 16, msgOffset)
            if self._COLORMODE == 'HTML':
                text = cgi.escape(text)
                self._htmlwrite(text)
            elif self._COLORMODE == 'TTY':
                self._write_tty(text, colorTag)
            else:
                self.fh.write(text)

        # Plain Text
        else:
            if type(text) == unicode:
                text = util.printableUnicode(text).encode('utf-8')
            elif encoding:
                try:
                    utext = unicode(text, encoding)
                    text = util.printableUnicode(utext).encode('utf-8')
                except:
                    text = util.printableText(str(text))
            else:
                text = util.printableText(str(text))
            if self._COLORMODE == 'HTML':
                text = cgi.escape(text)
                self._htmlwrite(text)
            elif self._COLORMODE == 'TTY':
                self._write_tty(text, colorTag)
            else:
                self.fh.write(text)

        # Close direction
        if self._COLORMODE == 'HTML' and direction != '':
            self._htmlwrite("</span>")

    def _write_tty(self, text, colorTag):

        # Write color escape sequences on every line (less -R requires this)
        for line in text.splitlines(True):
            _line = line.split('\n')
            self.fh.write(colorTag + _line[0] + '\x1b[0m')
            if len(_line) > 1:
                self.fh.write('\n')

    def _HTMLHeader(self, title="Dshell"):

        return """<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>""" + title + """</title>
<script>
function tsOnOff() {
var ss = document.styleSheets[0];
if (ss.cssRules) { var s = ss.cssRules[3].style; } else { var s = ss.rules[3].style; }
if (s.display != "none") { s.display = "none"; } else { s.display = "inline"; }
}
</script>
<style>
body {
font-family: monospace;
font-size: 10pt;
white-space: pre;
}
h1 {
font-family: helvetica;
font-size: 13pt;
font-weight: bolder;
white-space: pre;
}
h2 {
font-size: 12pt;
font-weight: bolder;
margin: 0 0;
white-space: pre;
}
DIV.timestamp {
color: green;
font-style: italic;
display: none;
}
@media print {
.np { display:none; }
}
</style>
</head>
<body><div class="np" style="position: fixed; right: 10px; top: 1px;">(<a href="javascript:tsOnOff();">Timestamp</a>)</div>
"""

    def _HTMLFooter(self):
        if self._htmldisplaytimes:
            display_timestamps = "<script>tsOnOff();</script>\n"
        else:
            display_timestamps = ''
        return display_timestamps + """</body>
</html>
"""

obj = ColorOutput
